package com.mmg.cosplay.utils;

import cn.hutool.core.text.UnicodeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.jhlabs.image.ImageUtils;
import com.mmg.cosplay.entity.PuzzleCaptcha;
import com.mmg.cosplay.entity.PuzzleGifCaptcha;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 滑块验证码工具类
 */
@Slf4j
@Component
public class CaptchaUtil {

    // 常量定义
    private static final int X_OFFSET = 8; // X轴偏移容差
    private static final int SPEED = 500;  // 最小验证时间（毫秒）
    private static final String CAPTCHA_PREFIX = "captcha:"; // 验证码缓存前缀
    private static final String CAPTCHA_RESULT_PREFIX = "captcha_result:"; // 验证码结果缓存前缀
    private static final long CACHE_EXPIRE_SECONDS = 5 * 60; // 缓存过期时间（5分钟）

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 获取验证码（PuzzleCaptcha）
     */
    public Map<String, Object> captcha(PuzzleCaptcha puzzleCaptcha) {
        String ip = getIP();
        String captchaKey = CAPTCHA_PREFIX + ip;
        String resultKey = CAPTCHA_RESULT_PREFIX + ip;

        // 删除上次验证结果
        stringRedisTemplate.delete(resultKey);

        Map<String, Object> captchaVo = new HashMap<>();
        captchaVo.put("image1", toDataUri(puzzleCaptcha.getArtwork(), "png"));
        captchaVo.put("image2", toDataUri(puzzleCaptcha.getVacancy(), "png"));

        // 存储验证码信息
        Map<String, String> cacheMap = new HashMap<>();
        cacheMap.put("x", String.valueOf(puzzleCaptcha.getX()));
        cacheMap.put("time", String.valueOf(System.currentTimeMillis()));
        cacheMap.put("width", String.valueOf(puzzleCaptcha.getWidth()));

        // 使用 Hash 存储验证码信息
        stringRedisTemplate.opsForHash().putAll(captchaKey, cacheMap);
        stringRedisTemplate.expire(captchaKey, CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);

        return captchaVo;
    }

    /**
     * 获取验证码（PuzzleGifCaptcha）
     */
    public Map<String, Object> captcha(PuzzleGifCaptcha puzzleCaptcha) {
        String ip = getIP();
        String captchaKey = CAPTCHA_PREFIX + ip;
        String resultKey = CAPTCHA_RESULT_PREFIX + ip;

        // 删除上次验证结果
        stringRedisTemplate.delete(resultKey);

        Map<String, Object> captchaVo = new HashMap<>();
        captchaVo.put("image1", toDataUri(puzzleCaptcha.getArtwork().toByteArray(), "png"));
        captchaVo.put("image2", toDataUri(puzzleCaptcha.getVacancy().toByteArray(), "png"));

        // 存储验证码信息
        Map<String, String> cacheMap = new HashMap<>();
        cacheMap.put("x", String.valueOf(puzzleCaptcha.getX()));
        cacheMap.put("time", String.valueOf(System.currentTimeMillis()));
        cacheMap.put("width", String.valueOf(puzzleCaptcha.getWidth()));

        // 使用 Hash 存储验证码信息
        stringRedisTemplate.opsForHash().putAll(captchaKey, cacheMap);
        stringRedisTemplate.expire(captchaKey, CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);

        return captchaVo;
    }

    /**
     * 验证码验证
     */
    public Map<String, Object> verify(Map<String, Object> map) {
        String ip = getIP();
        String captchaKey = CAPTCHA_PREFIX + ip;
        String resultKey = CAPTCHA_RESULT_PREFIX + ip;

        Map<String, Object> result = new HashMap<>();
        result.put("success", false);

        // 获取前端传来的参数
        Integer vx = toInt(map.get("x"));
        Integer width = toInt(map.get("width"), 1);

        // 从 Redis 获取缓存数据
        Map<Object, Object> cacheMap = stringRedisTemplate.opsForHash().entries(captchaKey);
        if (cacheMap == null || cacheMap.isEmpty()) {
            return result;
        }

        Integer x = toInt(cacheMap.get("x"));
        Integer realWidth = toInt(cacheMap.get("width"));
        Long time = toLong(cacheMap.get("time"));
        long s = System.currentTimeMillis() - time;

        // 计算缩放比例
        double ratio = NumberUtil.div(realWidth, width).doubleValue();

        // 验证逻辑
        if (x == null || vx == null) {
            stringRedisTemplate.delete(captchaKey);
            return result;
        } else if (Math.abs(x - (vx * ratio)) > X_OFFSET * ratio || s < SPEED) {
            stringRedisTemplate.delete(captchaKey);
            return result;
        }

        // 验证成功
        result.put("success", true);
        stringRedisTemplate.delete(captchaKey);
        stringRedisTemplate.opsForValue().set(resultKey, "true", CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);

        return result;
    }

    /**
     * 将image对象转为字节
     *
     * @param image
     * @param format
     * @return
     */
    public static byte[] toBytes(Image image, String format) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        try {
            ImageIO.write(ImageUtils.convertImageToARGB(image), format, stream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return stream.toByteArray();
    }

    /**
     * 将image对象转为base64字符串
     *
     * @param image
     * @return
     */
    public static String toBase64(Image image, String format) {
        return Base64.getEncoder().encodeToString(toBytes(image, format));
    }

    /**
     * 将image对象转为前端img标签识别的base64字符串
     *
     * @param image
     * @param format
     * @return
     */
    public static String toDataUri(Image image, String format) {
        return String.format("data:image/%s;base64,%s", format, toBase64(image, format));
    }

    /**
     * 将image对象转为前端img标签识别的base64字符串
     *
     * @param bytes
     * @param format
     * @return
     */
    public static String toDataUri(byte[] bytes, String format) {
        return String.format("data:image/%s;base64,%s", format, Base64.getEncoder().encodeToString(bytes));
    }

    /**
     * 将 Object 转换为 Integer
     */
    public static Integer toInt(Object value) {
        return toInt(value, null);
    }

    /**
     * 将 Object 转换为 Integer
     */
    public static Integer toInt(Object value, Integer defaultValue) {
        if (value == null) return defaultValue;
        try {
            return Integer.parseInt(value.toString());
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }

    /**
     * 将 Object 转换为 Long
     */
    public static Long toLong(Object value) {
        if (value == null) return null;
        try {
            return Long.parseLong(value.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * 获取IP地址
     */
    private String getIP() {
        try {
            String response = HttpUtil.get("https://api.bilibili.com/x/web-interface/zone?jsonp=jsonp");
            String json = UnicodeUtil.toString(response);
            return JSONUtil.parseObj(json).getJSONObject("data").getStr("addr");
        } catch (Exception e) {
            return "127.0.0.1";
        }
    }

    /**
     * 检查验证码是否已通过验证
     *
     * @return 是否通过验证
     */
    public boolean checkCaptchaVerified() {
        String ip = getIP();
        String resultKey = CAPTCHA_RESULT_PREFIX + ip;
        String result = stringRedisTemplate.opsForValue().get(resultKey);
        return "true".equals(result);
    }

    /**
     * 清除验证结果
     */
    public void clearCaptchaResult() {
        String ip = getIP();
        String resultKey = CAPTCHA_RESULT_PREFIX + ip;
        stringRedisTemplate.delete(resultKey);
    }
}
